package mail;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptionTest {
  KeyGenerator keyGen = null;
  Cipher cipher = null;
  public static void main(String[] args) {
    try {
      EncryptionTest test = new EncryptionTest();
      Key key = test.generateKey();
      Cipher cipher = test.initCipher(Cipher.ENCRYPT_MODE, key);
      String msg = "zhangjunyong";
      byte[] encrypt = cipher.doFinal(msg.getBytes());
      System.out.println(new String(encrypt));
      cipher = test.initCipher(Cipher.DECRYPT_MODE, key);
      byte[] decrypt = cipher.doFinal(encrypt);
      System.out.println(new String(decrypt));
    } catch(Exception ex) {
      ex.printStackTrace();
    }
  }
  
  public Key generateKey() {
    try {
      SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
      byte[] keyData = {1, 3, 5, 1, 7, 4, 1, 2};
      DESKeySpec keySpec = new DESKeySpec(keyData);
      return factory.generateSecret(keySpec);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    } 
  }
  
  public void test() {   
    Key key = genKey();
    initCipher(Cipher.ENCRYPT_MODE, key);
    try {
      FileInputStream fis = new FileInputStream("a.txt");
      FileOutputStream fos = new FileOutputStream("b.txt");
      crypt(fis, fos);
      fis.close();
      fos.close();
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    initCipher(Cipher.DECRYPT_MODE, key);
    try {
      FileInputStream fis = new FileInputStream("b.txt");
      FileOutputStream fos = new FileOutputStream("c.txt");
      crypt(fis, fos);
      fis.close();
      fos.close();
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  public void crypt(InputStream is, OutputStream os) {
    try {
      int blockSize = cipher.getBlockSize();
      int outputSize = cipher.getOutputSize(blockSize);
      System.out.println("blockSize:" + blockSize);
      System.out.println("outputSize:" + outputSize);
      byte[] in = new byte[blockSize];
      byte[] out = new byte[outputSize];
      
      int length = 0;
      boolean more = true;
      while(more) {
        length = is.read(in);
        if(length == blockSize) {
          cipher.update(in, 0, length, out);
          os.write(out);
        }else {
          more = false;
        }
      }
      if(length > 0) {
        out = cipher.doFinal(in, 0, length);
      }else {
        out = cipher.doFinal();
      }
      os.write(out);
    } catch(Exception ex) {
      ex.printStackTrace();
    }
  }
  
  public byte[] encryptString(byte[] in) {
    int inSize = cipher.getBlockSize();
    int outSize = cipher.getOutputSize(inSize);
    System.out.println("cipher mode:" + cipher);
    System.out.println("blockSize:" + inSize);
    System.out.println("outSize:" + outSize);
    byte[] tmp = in;
    System.out.println("in String:" + in);
    System.out.println("in String Size:" + tmp.length);
    System.out.println(Arrays.toString(tmp) + ":" +  tmp.length);
    int i=0, j=0;
    byte[] inByte = new byte[inSize];
    byte[] outByte = new byte[outSize];
    byte[] tmp1 = null;
    if(tmp.length >= inSize) {
      tmp1 = new byte[outSize*(tmp.length/inSize)];
      while(tmp.length-i >= inSize) {
        inByte = Arrays.copyOfRange(tmp, i, i+inSize);
        i=i+inSize;
        System.out.println("i:" + i);
        try {
          System.out.println(Arrays.toString(inByte) + ":" +  inByte.length);
          cipher.update(inByte, 0, inSize, outByte);
          System.out.println(Arrays.toString(outByte) + ":" +  outByte.length);
          for(int t=0; t<outSize; t++) {
            tmp1[j++] = outByte[t];
          }
        } catch (ShortBufferException e) {
          e.printStackTrace();
          return null;
        }
      }
    }
    try {
      if(i < tmp.length) {
        inByte = Arrays.copyOfRange(tmp, i, tmp.length);
        outByte = cipher.doFinal(inByte);
        System.out.println(Arrays.toString(inByte) + ":" +  inByte.length);
        System.out.println(Arrays.toString(outByte) + ":" +  outByte.length);
      }else {
        outByte = cipher.doFinal();
        System.out.println(Arrays.toString(outByte) + ":" +  outByte.length);
      }
      int size = 0;
      if(tmp1 != null) {
        size += tmp1.length;
      }
      if(outByte != null && outByte.length > 0) {
        System.out.println("outByte Size:" + outByte.length);
        size += outByte.length;
      }
      System.out.println("result Size:" + size);
      byte[] out = new byte[size];
      int h = 0;
      if(tmp1 != null) {
        for(int t=0; t<tmp1.length; t++ ) {
          out[h++] = tmp1[t];
        }
      }
      if(outByte != null && outByte.length > 0) {
        for(int t=0; t<outByte.length; t++ ) {
          out[h++] = outByte[t];
        }
      }
      System.out.println(Arrays.toString(out) + ":" +  out.length);
      return out;
    } catch (IllegalBlockSizeException e) {
      e.printStackTrace();
      return null;
    } catch (BadPaddingException e) {
      e.printStackTrace();
      return null;
    }
  }
  
  public Key genKey() {    
    try {
      keyGen = KeyGenerator.getInstance("AES");
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return null;
    }
    SecureRandom random = new SecureRandom();
    keyGen.init(random);
    Key key = keyGen.generateKey();
    return key;
  }
  
  public Cipher initCipher(int mode, Key key) {
    try {
      cipher = Cipher.getInstance("DES");
      cipher.init(mode, key);
    } catch (InvalidKeyException e) {
      e.printStackTrace();
      return null;
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return null;
    } catch (NoSuchPaddingException e) {
      e.printStackTrace();
      return null;
    }
    return cipher;
  }
}
